1.sol

Solidity Array Management in Smart Contracts

In this Solidity example, we explore how to manage arrays within a smart contract.

  1. Declaring Arrays:

    • arr_1: A dynamic array of unsigned integers (uint256[]), initially empty.
    • arr_2: Another dynamic array, initialized with three values [3, 2, 4].
    • arr_3: A fixed-size array of 5 uint256 elements, but uninitialized.
  2. Getting Value from an Array:
    The getValueOfIndex function allows the user to retrieve an element from arr_2 at a specific index.

    • Task: Add a check to ensure the index is within the array’s bounds to avoid errors.

    Example of adding a check:

    require(_index < arr_2.length, "Index out of bounds");
    
  3. Adding to the Array:
    The addToArray function demonstrates how to dynamically add elements to arr_2 using the push() method. This method appends a new value to the end of the array.

Improvements:


2.sol

Constructors and Inheritance in Solidity

In this example, we’re exploring how to use constructors and inheritance in Solidity.

  1. Constructor in MyConstructor Contract:

    • The constructor is a special function that runs once when the contract is deployed. It initializes the state variable name with the value passed during deployment.
    constructor(string memory _name) {
        name = _name;
    }
    
  2. Creating Getter and Setter Functions:
    To access and modify the name variable, we’ll create getter and setter functions.

    • Getter: Returns the current value of name.
    function getName() public view returns (string memory) {
        return name;
    }
    
    • Setter: Allows the contract owner to update the name.
    function setName(string memory _newName) public {
        name = _newName;
    }
    
  3. Inheritance in MySecondContract:
    The MySecondContract inherits from MyConstructor. It calls the parent contract’s constructor to initialize the name variable using the MyConstructor(_name) syntax.

    • The constructor in MySecondContract simply passes the provided _name to the parent MyConstructor's constructor.
    constructor(string memory _name) MyConstructor(_name) {}
    

Final Code

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract MyConstructor {
    
    string public name;
    
    constructor(string memory _name) {
        name = _name;
    }

    // Getter function for 'name'
    function getName() public view returns (string memory) {
        return name;
    }
    
    // Setter function for 'name'
    function setName(string memory _newName) public {
        name = _newName;
    }
}

contract MySecondContract is MyConstructor {
    constructor(string memory _name) MyConstructor(_name) {}
}

Key Concepts:


3.sol

Interacting Between Contracts in Solidity

This Solidity example demonstrates how one contract can interact with another by calling its functions.

  1. Contract MyOtherContract:

    • This contract contains a public state variable age, initialized to 29.
    • It also has a function getAge() that returns the value of age.
    uint256 public age = 29;
    
    function getAge() public view returns (uint256) {
        return age;
    }
    
  2. Contract CONTRACTB:

    • The getAgeFromOtherContract function allows CONTRACTB to interact with another contract (MyOtherContract) by using its address.
    • We pass the address of the MyOtherContract instance, cast it as MyOtherContract, and call its getAge() function.

    Key steps:

    • Use the contract’s address (_contractAddress) to interact with its public functions.
    • The function getAgeFromOtherContract retrieves the age from the other contract.
    function getAgeFromOtherContract(address _contractAddess) public view returns (uint256) {
        MyOtherContract other = MyOtherContract(_contractAddess);
        uint256 age = other.getAge();
        return age;
    }
    

Key Concepts:

Example Use Case:

If you deploy MyOtherContract and pass its address to CONTRACTB, the latter can fetch the age value from MyOtherContract and return it.


4.sol

Errors, Checks, and Conditions in Solidity

In this Solidity example, we will learn how to use require, revert, and assert to handle errors and add necessary checks to a contract. Additionally, we’ll implement a time-based restriction for a function.

  1. Require Statement:

    • The require function ensures that certain conditions are met before the execution of the function proceeds.
    • In myFunc, we check if _x is smaller than _y. If not, an error message "X is bigger than Y" is thrown.
    require(_x < _y, "X is bigger that Y");
    
  2. Revert:

    • The revert function is used to explicitly abort a function when a certain condition is met.
    • In myPureRevertFunc, if _x == 10, the transaction will revert with the message "X is 10".
    if(_x == 10) {
        revert("X is 10");
    }
    
  3. Assert:

    • The assert function is used to test for conditions that should never fail.
    • In checkMax, we ensure that maxAmount is always 100. If it is not, the contract will halt execution with an error.
    assert(maxAmount == 100);
    
  4. Adding a Timestamp Check:

    • We can add a time-based check using Solidity’s block.timestamp to restrict function calls to within 2 days of a particular event.
    • For this example, we will modify myFunc to ensure it can only be called within a 2-day window after the contract deployment (or after a certain point).

    Here’s how you can add the check in myFunc:

    uint256 public startTime;
    
    constructor() {
        startTime = block.timestamp;
    }
    
    function myFunc(uint256 _x, uint256 _y) public view returns (uint256 xy) {
        require(block.timestamp <= startTime + 2 days, "Function can only be called within 2 days");
        require(_x < _y, "X is bigger than Y");
        checkMax();
        return _x + _y;
    }
    
    • startTime stores the block timestamp when the contract is deployed.
    • The require(block.timestamp <= startTime + 2 days) ensures that myFunc can only be called within 2 days after the contract is deployed.

Final Code Example

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract MyErrorsAndChecks {
    uint256 public maxAmount = 100;
    uint256 public startTime;
    
    constructor() {
        startTime = block.timestamp;
    }

    function updateMax() public {
        maxAmount = 50;
    }
    
    function myFunc(uint256 _x, uint256 _y) public view returns (uint256 xy) {
        // Allow calling only within 2 days from contract deployment
        require(block.timestamp <= startTime + 2 days, "Function can only be called within 2 days");
        require(_x < _y, "X is bigger than Y");
        checkMax();
        return _x + _y;
    }
   
    function myPureRevertFunc(uint256 _x, uint256 _y) public pure returns (uint256 xy) {
        if(_x == 10) {
            revert("X is 10");
        }
        return _x + _y;
    }
   
    function checkMax() internal view {
        assert(maxAmount == 100);
    }
}

Key Concepts:


5.sol

Enums, Access Control, and Function Updates in Solidity

In this Solidity example, we use an enum to represent different levels of rarity and implement access control to allow only an admin to update the rarity. We’ll also modify the function to accept a parameter for setting any rarity and add a getter function to retrieve the current rarity without access control.

Key Features:

  1. Enum Definition:

    • enum Rarity { original, rare, super_rare }: Defines three types of rarity.
    • Each type has an underlying index, starting from 0 for original, 1 for rare, and 2 for super_rare.
  2. Access Control:

    • We’ll restrict the makeSuperRare (or any function that updates rarity) to be callable only by the admin. For simplicity, we’ll define the contract deployer as the admin.
  3. Update to Accept Rarity Type:

    • Instead of hardcoding the rarity update to super_rare, we’ll modify the function to accept any Rarity type as a parameter.
  4. Get Function:

    • A simple getter function will return the current rarity value.

Code Implementation:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract MyEnums {
    
    // Define an enum with 3 rarity types
    enum Rarity {
        original,  // 0
        rare,      // 1
        super_rare // 2
    }
    
    Rarity public rarity; // State variable to store the current rarity
    
    address public admin; // Variable to store the admin address

    // Constructor sets the initial rarity and defines the admin as the contract deployer
    constructor() {
        rarity = Rarity.rare; // Default to rare
        admin = msg.sender; // Set contract deployer as admin
    }
    
    // Function to allow admin to update the rarity to any type
    function makeSuperRare(Rarity _rarity) public {
        require(msg.sender == admin, "Only admin can update rarity");
        rarity = _rarity;
    }
    
    // Getter function to retrieve the current rarity without any access check
    function getCurrentRarity() public view returns (Rarity) {
        return rarity;
    }
}

Breakdown:

  1. Admin Access Control:

    • The require(msg.sender == admin) ensures that only the address that deployed the contract (the admin) can call the makeSuperRare function.
  2. Function to Set Any Rarity:

    • makeSuperRare now accepts a parameter of type Rarity:
      function makeSuperRare(Rarity _rarity) public {
          require(msg.sender == admin, "Only admin can update rarity");
          rarity = _rarity;
      }
      
    • This allows the admin to update the rarity to any value from the Rarity enum (either original, rare, or super_rare).
  3. Getter for Rarity:

    • The getCurrentRarity function returns the current value of rarity without any checks.
      function getCurrentRarity() public view returns (Rarity) {
          return rarity;
      }
      

Key Concepts:


6.sol

Events in Solidity: Emitting Logs

In this Solidity example, we explore how to use events to log important contract actions on the blockchain.

  1. Event Declaration:

    • event CreatedNFT(address indexed user, uint256 id, uint256 dna); defines an event that records the address of the user who created the NFT, along with its ID and DNA.
    • The indexed keyword allows us to filter logs by the user address, making it easier to search for specific events.
  2. Emitting the Event:

    • Inside the createNft function, the event CreatedNFT is emitted. This generates a log on the blockchain when the function is called.
    • The msg.sender represents the address of the caller (the user creating the NFT).

Code Example:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract MyEvents {
    
    // Declare the event with indexed user
    event CreatedNFT(address indexed user, uint256 id, uint256 dna);
    
    // Function to create an NFT and emit the event
    function createNft(uint256 _id, uint256 _dna) public {
        // In real-world scenario, NFT creation logic would go here
        
        // Emit the event to log the creation
        emit CreatedNFT(msg.sender, _id, _dna);
    }

}

Key Concepts:

  1. Events:

    • Events allow you to write logs to the blockchain, which are stored but not accessible on-chain by smart contracts. They are primarily used to communicate information from the blockchain to external applications like DApps.
  2. Indexed Parameters:

    • The indexed keyword allows the user address (msg.sender) to be searchable when querying the logs. Only up to 3 parameters can be indexed in an event.
  3. Emitting Events:

    • emit CreatedNFT(msg.sender, _id, _dna) broadcasts the event with the provided data, which can be accessed by off-chain applications like web3 or any blockchain explorer.

Practical Use:


7.sol

Functions in Solidity: Types and Use Cases

This Solidity example demonstrates various types of functions in a smart contract, including pure, view, internal, external, and functions that return values.

Key Components:

  1. State Variables:

    • myUint, myString, myBool, and myArr are state variables stored on the blockchain.
    • These variables represent different types of data: uint256, string, bool, and an array of uint256.
  2. Pure Function:

    • A pure function doesn’t read or modify the contract’s state; it only performs computations based on input parameters.
    • Example: myPureFunc adds two integers without accessing any contract variables.
    function myPureFunc(uint256 _x, uint256 _y) public pure returns (uint256 xy) {
        return _x + _y;
    }
    
  3. View Function:

    • A view function reads from the contract’s state but doesn’t modify it. In this example, myViewFunc reads the value of myString but doesn’t change it.
    • This function is marked as internal, meaning it can only be called within the contract or by derived contracts.
    function myViewFunc() internal view returns (string memory) {
        return myString;
    }
    
  4. State-Modification Function:

    • myUpdateFunc is a public function that updates the state variable myString and uses the myViewFunc to return the updated value. This shows how internal functions can be called within the same contract.
    function myUpdateFunc() public returns (string memory) {
        myString = "LPU";
        string memory savedString = myViewFunc();
        return savedString;
    }
    
  5. Function Returning Multiple Values:

    • myReturnsFunc is an external function that returns multiple state variables at once: myUint, myString, myBool, and myArr.
    function myReturnsFunc() external view returns (uint256, string memory, bool, uint256[] memory) {
        return (myUint, myString, myBool, myArr);
    }
    
  6. Function with No Return:

    • myNoReturnFunc is an external function that simply updates the state variable myBool to false without returning any value.
    function myNoReturnFunc() external {
        myBool = false;
    }
    

Final Code Summary:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract MyFunctions {
    
    uint256 myUint = 42;
    string myString = "Piyush";
    bool myBool = true;
    uint256[] myArr = [3, 3, 3];
    
    // Pure function - No state read/write
    function myPureFunc(uint256 _x, uint256 _y) public pure returns (uint256 xy) {
        return _x + _y;
    }
    
    // Internal view function - Reads state, no modification
    function myViewFunc() internal view returns (string memory) {
        return myString;
    }
    
    // Public function - Modifies state, calls internal view function
    function myUpdateFunc() public returns (string memory) {
        myString = "LPU";
        string memory savedString = myViewFunc();
        return savedString;
    }
    
    // External function - Returns multiple state variables
    function myReturnsFunc() external view returns (uint256, string memory, bool, uint256[] memory) {
        return (myUint, myString, myBool, myArr);
    }
    
    // External function - Modifies state, no return
    function myNoReturnFunc() external {
        myBool = false;
    }
}

Key Concepts:


8.sol

Simple if else


9.sol

Inheritance in Solidity with Function Overrides

In this Solidity example, we explore inheritance and function overriding. In Solidity, contracts can inherit from other contracts, and child contracts can override parent functions.

Key Components:

  1. Base Contract (MyInheritance_A):

    • The contract defines an internal state variable name set to "LPU".
    • The internal keyword allows this variable to be accessible in derived contracts but not from outside the contract itself.
  2. Intermediate Contract (MyInheritance_B):

    • This contract inherits from MyInheritance_A and defines the getName function, which returns the value of name.
    • The function is marked virtual, which allows it to be overridden by child contracts.
  3. Derived Contract (MyInheritance_C):

    • Inherits from MyInheritance_B.
    • It overrides the getName function and returns a different value, "Daniel".
    • The override keyword ensures that the function is recognized as an override.

Code Example:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// Base contract
contract MyInheritance_A {
    string internal name = "LPU";  // Internal variable accessible in child contracts
}

// Intermediate contract inheriting from MyInheritance_A
contract MyInheritance_B is MyInheritance_A {
    
    // Virtual function that can be overridden in derived contracts
    function getName() public view virtual returns (string memory) {
        return name;  // Returns the inherited name "LPU"
    }
}

// Derived contract inheriting from MyInheritance_B and overriding getName
contract MyInheritance_C is MyInheritance_B  {
    
    // Override the getName function to return a different value
    function getName() public view virtual override returns (string memory) {
        return "Daniel";  // Returns "Daniel" instead of "LPU"
    }
}

Key Concepts:

  1. Inheritance:

    • MyInheritance_B inherits from MyInheritance_A, and MyInheritance_C inherits from MyInheritance_B. Each child contract has access to the parent’s state variables and functions.
    • Inheritance enables code reusability by allowing contracts to share functionality.
  2. Function Overriding:

    • The getName function in MyInheritance_B is marked as virtual, allowing it to be overridden in derived contracts.
    • In MyInheritance_C, the getName function is marked as override, which indicates that this function replaces the parent’s implementation.
  3. Internal Visibility:

    • The internal keyword used for the name variable ensures that it is accessible in derived contracts (MyInheritance_B and MyInheritance_C) but not directly from outside the contract.

Practical Use:


10.sol

Interfaces in Solidity: Implementation Example

This Solidity example demonstrates how to implement an interface using the ICounter interface and the MyInterface contract.

Key Concepts:

  1. Interface Definition (ICounter):

    • An interface in Solidity is like a blueprint. It defines functions but does not provide their implementation.
    • The ICounter interface has two functions:
      • count(): Returns the current counter value.
      • addToCount(): Increases the counter by 1.
    • Interfaces are used to enforce that contracts implementing them provide certain functionalities.
  2. Contract Implementation (MyInterface):

    • The MyInterface contract implements the ICounter interface by providing concrete implementations for count() and addToCount().
    • The contract uses the keyword override to indicate that these functions are fulfilling the interface’s requirements.
    • It maintains an internal counter variable to track the count.

Code Example:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// Interface declaration
interface ICounter {
    function count() external view returns (uint256);
    function addToCount() external;
}

// Contract implementing the interface
contract MyInterface is ICounter {

    uint256 counter = 0;  // State variable to store the count
    
    // Override the count function to return the counter value
    function count() external view override returns (uint256) {
        return counter;
    }
    
    // Override the addToCount function to increment the counter
    function addToCount() external override {
        counter++;
    }
}

Key Concepts:

  1. Interfaces:

    • Interfaces define the functions that a contract must implement. They do not have state variables or implementation details.
    • Functions in interfaces are automatically external and must be marked as override in the implementing contract.
  2. Implementing an Interface:

    • The contract MyInterface implements the ICounter interface by providing the actual logic for both count() and addToCount().
    • The count() function is marked view because it only reads the counter value.
    • The addToCount() function updates the state by incrementing counter.
  3. Override Keyword:

    • When implementing functions from an interface, you must use the override keyword to explicitly state that the function is fulfilling the interface’s definition.

Practical Use: